from pathlib import Path
from typing import Union, List, Callable, Any
from PIL import Image  # 导入 Pillow 库

import fitz
from PyQt6.QtCore import QThread, pyqtSignal
from PyQt6.QtWidgets import QWidget, QFileDialog
from qfluentwidgets import GroupHeaderCardWidget, BodyLabel, PushButton, FluentIcon as FIF, PrimaryPushButton, InfoBar, \
    SwitchButton

from common import OfficeIcon
from components import BaseScrollArea, FileListSettingCard


class PdfToImageAndMergeImage:
    log_func: Callable = print

    def __init__(
            self,
            files: Union[Path, str, List[Union[Path, str]]],
            out_dir: Union[Path, str],
            merge: bool = False,
            log_func: Callable = print
    ):
        if isinstance(files, str):
            files = Path(files)
        if isinstance(files, Union[Path, str]):
            files = [files]

        if isinstance(out_dir, str):
            out_dir = Path(out_dir)
        out_dir.mkdir(parents=True, exist_ok=True)

        self.files = files
        self.out_dir = out_dir
        self.merge = merge
        self.log_func = log_func

    @classmethod
    def resize(cls, image: Image.Image, width: int):
        """
        调整图片大小
        :param image: 图片对象
        :param width: 宽度
        :return: 调整后的图片对象
        """
        w, h = image.size
        if image.mode == 'RGB':
            image = image.convert('RGBA')
        return image.resize((width, width / w * h), resample=Image.Resampling.LANCZOS)

    @classmethod
    def merge_images(cls, images: List[str], out_file: Union[str, Path], name: str) -> None:
        """
        合并图片
        :param images: 图片路径列表
        :param out_file: 输出文件路径
        :return: None
        """
        cls.log_func(f'开始合并 {name} 的所有图片')
        imgs = [Image.open(img) for img in images]
        widths, heights = zip(*(i.size for i in imgs))
        max_width = max(widths)
        total_height = sum(heights)
        new_im = Image.new('RGBA', (max_width, total_height))
        y_offset = 0
        for index, im in enumerate(imgs):
            im: Image.Image
            re_im = cls.resize(im, max_width)
            new_im.paste(re_im, (0, y_offset))
            y_offset += im.size[1]
            cls.log_func(f'合并进度：{index + 1}/{len(imgs)}')
        new_im.save(out_file, 'PNG')
        cls.log_func(f'已合并 {name} 的所有图片')

    def pdf_to_image(
            self,
            pdf_file: Union[str, Path],
            out_dir: Union[str, Path]
    ) -> list:
        """
        将PDF文件转换为图片文件
        :param pdf_file: PDF文件路径
        :param out_dir: 图片保存路径
        :return: None
        """
        pdfDoc = fitz.open(pdf_file)
        self.log_func(f'开始转换{pdf_file}为图片')
        images = []  # 用于存储当前 PDF 文件转换后的图片
        for pg in range(pdfDoc.page_count):
            page = pdfDoc[pg]
            rotate = int(0)
            # 每个尺寸的缩放系数为1.3，这将为我们生成分辨率提高2.6的图像。
            # 此处若是不做设置，默认图片大小为：792X612, dpi=96
            zoom_x = 1.33333333  # (1.33333333-->1056x816)   (2-->1584x1224)
            zoom_y = 1.33333333
            mat = fitz.Matrix(zoom_x, zoom_y).prerotate(rotate)
            pix = page.get_pixmap(matrix=mat, alpha=False)
            # 图片保存路径
            # 图片文件名格式：[pdf文件名]-[页码].jpg
            # 如：test.pdf-1.jpg
            pdf_file_name = Path(pdf_file).stem
            img_path = (out_dir / f'[{pdf_file_name}]-{pg}.jpg').as_posix()
            pix.save(img_path)  # 将图片写入指定的文件夹内
            self.log_func(f'已转换 {pdf_file} 第 {pg + 1} 页为图片')
            images.append(img_path)  # 读取图片并添加到列表中
        pdfDoc.close()
        return images

    def _run(self) -> None:
        for pdf_file in self.files:
            pdf_file: Path
            name = Path(pdf_file).stem
            images = self.pdf_to_image(pdf_file, self.out_dir)
            if self.merge:
                # 合并图片
                self.merge_images(images, self.out_dir / f'{name}.jpg', name)

    @classmethod
    def run(
            cls,
            files: Union[Path, str, List[Union[Path, str]]],
            out_dir: Union[Path, str],
            merge: bool = False,
            log_func: Callable = print
    ) -> None:
        """
        运行转换
        :param files:
        :param out_dir:
        :param merge:
        :param isShowLog:
        :return:
        """
        PdfToImageAndMergeImage(files, out_dir, merge, log_func)._run()


class PdfToImageView(BaseScrollArea):
    title = 'PDF转图片'
    description = '将PDF文件转换为图片文件'
    icon = OfficeIcon.MARK
    object_name = 'PdfToImageView'

    def __init__(self, parent: QWidget = None):
        super().__init__(parent)
        self.setReturnVisible(True)
        self.titleLabel = BodyLabel(self.title, self)
        self.pdfFiles = FileListSettingCard(OfficeIcon.PDF, 'PDF文件', 'PDF文件(*.pdf)', self)
        self.headerCard = GroupHeaderCardWidget('基本设置', self)
        self.folderBtn = PushButton('选择文件夹', self, FIF.FOLDER_ADD)
        self.mergeBtn = SwitchButton(self)
        self.startBtn = PrimaryPushButton(FIF.PLAY, '开始转换', self)

        self.folderCard = self.headerCard.addGroup(FIF.FOLDER, '保存路径', '图片保存路径', self.folderBtn)
        self.mergeCard = self.headerCard.addGroup(OfficeIcon.JPG, '合并图片',
                                                  '是否合并PDF文件的所有图片，每个PDF转为一个图片', self.mergeBtn)
        self.headerCard.addGroup(FIF.PLAY_SOLID, '开始转换', '将PDF文件转换为图片文件', self.startBtn)

        self.__initWidget()

    def __initWidget(self):
        self.scrollLayout.addWidget(self.titleLabel)
        self.scrollLayout.addWidget(self.pdfFiles)
        self.scrollLayout.addWidget(self.headerCard)

        self.folderBtn.clicked.connect(self.selectFolder)
        self.startBtn.clicked.connect(self.startConvert)

    def selectFolder(self):
        folder = QFileDialog.getExistingDirectory(self, '选择文件夹', './')
        if folder:
            self.folderCard.setContent(folder)

    def startConvert(self):
        files = self.pdfFiles.files
        out_dir = self.folderCard.content()
        if not files:
            InfoBar.error('', '请选择PDF文件', duration=3000, parent=self)
            return
        if not Path(out_dir).exists():
            InfoBar.error('', f'保存路径{out_dir}不存在', duration=3000, parent=self)
            return
        self.load(
            PdfToImageAndMergeImage.run,
            files,
            out_dir,
            merge=self.mergeBtn.isChecked(),
            isShowLog=True
        )

    def onFuncSuccess(self, result: Any):
        InfoBar.success('', f'转换成功，图片保存在{self.folderCard.content()}', duration=3000, parent=self)


if __name__ == '__main__':
    import os

    kivy_path = r'C:\Users\26406\Downloads\photo\Kivy'
    jpgs = [Path(kivy_path, f).as_posix() for f in os.listdir(kivy_path)]
    jpgs.sort(key=lambda x: int(Path(x).stem.split('-')[-1]))
    PdfToImageAndMergeImage.merge_images(jpgs, r'C:\Users\26406\Downloads\photo\Kivy\merge.jpg')
